#include <iostream>
#include <string>
using namespace std;

class Person
{
private:
	string _name = "Peter";
	int age = 18;

public:
	void Print()
	{
		cout << "姓名：" << _name << "年龄：" << age << endl;
	}
};

// 继承后的父类的Persion成员都会变成子类的一部分
// 这体现了Student和Persion的复用
class Student : public Person
{
public:
	void PrintPersion()
	{
		// 类的私有和保护在当前类中没区别
		// 在继承后的子类中有差别：私有的在子类中不可用
		// cout << _name << _age << endl;
	}

protected:
	int _stuid; // 学号
};

class Teacher : public Person
{
public:
protected:
	int _jobid; // 工号
};

// 子类和父类之前的赋值
void test1()
{
	Person p;
	Student s;

	// 1、子类对象可以赋值给父类对象/指针/引用——切片
	p = s;
	Person &ref = s;
	Person *ptr = &s;
	// 反过来可以吗——大部分不行，但有一种情况可以
	// 这个父类指针是指向子类对象的，强转之后可以赋值
	Student *sptr = (Student *)ptr;
	// Print();
}

class Person2
{
protected:
	string _name = "Peter";
	int _age = 18;

public:
	void Print()
	{
		cout << "姓名：" << _name << "年龄：" << _age << endl;
	}
};

class Student2 : public Person2
{
public:
	void Print()
	{
		cout << "姓名：" << _name << "年龄：" << _age << endl;
	}

protected:
	// 与父类定义同名变量不会报错，因为在不同作用域
	int _age = 20;
};

void test2()
{
	Student2 s;
	// 当父类和子类同时有同名成员时，子类的成员隐藏了父类的成员(重定义)
	// 不算重载，因为重载需要同意作用域
	s.Print();
	// 除非指定作用域：s.Person2::Print()
	s.Person2::Print();
}

class Person3
{
public:
	Person3(const char *name = "peter")
			: _name(name)
	{
		cout << "Person3()" << endl;
	}

	Person3(const Person3 &p)
			: _name(p._name)
	{
		cout << "Person(const Person3& p)" << endl;
	}

	Person3 &operator=(const Person3 &p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;

		return *this;
	}
	// 别看名字不一样，子类的析构函数和父类的习惯函数也构成隐藏，和多态有关
	~Person3()
	{
		cout << "~Person3()" << endl;
	}

protected:
	string _name; // 姓名
};

class Student3 : public Person3
{
public:
	Student3(const char *name, int num)
			: Person3(name), _num(num)
	{
		cout << "Student3()" << endl;
	}

	Student3(const Student3 &s)
			// 子类可以赋值给父类
			: Person3(s), _num(s._num)
	{
		cout << "Student3(const Student3& s)" << endl;
	}

	Student3 &operator=(const Student3 &s)
	{
		cout << "Student3& operator= (const Student3& s)" << endl;
		if (this != &s)
		{
			Person3::operator=(s);
			_num = s._num;
		}
		return *this;
	}

	~Student3()
	{
		// 不需要显示调用父类的析构，因为结束后会自动调用父类的析构
		// Person3::~Person3();
		cout << "~Student3()" << endl;
	}

protected:
	int _num; // 学号
};

// 派生类的默认成员函数
void test3()
{
	// 派生类的构造不能自己初始化父类的成员变量，需要调用父类的构造函数Person3(name)
	// 可以不用主动写Person3(name)，编译器会自动调用，前提是父类的构造函数是缺省的
	Student3 s1("jack", 18);
	Student3 s2(s1);
	Student3 s3("rose", 17);
	s1 = s3;
}

// 静态变量的继承
class Person4
{
public:
	Person4() { ++_count; }

public:
	string _name;
	static int _count;
};
int Person4::_count = 0;
class Student4 : public Person4
{
protected:
	int sum;
};

void test4()
{
	Person4 p;
	Student4 s;
	p._name = "jack";
	s._name = "rose";
	p._count = 1;
	s._count = 2;

	// 改变的是同一个count，因为静态属于类
	cout << p._name << endl;
	cout << s._name << endl;
	cout << p._count << endl;
	cout << s._count << endl;
}

// C++特有的菱形继承问题
class Person5
{
public:
	string _name; // 姓名
};

class Student5 : public Person5
{
protected:
	int _num; // 学号
};
class Teacher5 : public Person5
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student5, public Teacher5
{
protected:
	string _majorCourse; // 主修课程
};

void test5()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	//a._name = "peter";

	// 需要显示指定访问哪个父类的成员可以解决二义性问题，但是数据冗余问题无法解决
	a.Student5::_name = "xxx";
	a.Teacher5::_name = "yyy";
	cout << a.Student5::_name << endl;
	cout << a.Teacher5::_name << endl;
}

//解决菱形继承的二义性和冗余方法：虚继承
class Person6
{
public:
	string _name; // 姓名
};

class Student6 : virtual public Person6
{
protected:
	int _num; // 学号
};
class Teacher6 : virtual public Person6
{
protected:
	int _id; // 职工编号
};
class Assistant2 : public Student6, public Teacher6
{
protected:
	string _majorCourse; // 主修课程
};

void test6()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant2 a;
	a._name = "peter";
	cout << a._name << endl;

	// 需要显示指定访问哪个父类的成员可以解决二义性问题，但是数据冗余问题无法解决
	a.Student6::_name = "xxx";
	a.Teacher6::_name = "yyy";
	cout << a._name << endl;
	cout << a.Student6::_name << endl;
	cout << a.Teacher6::_name << endl;
}

int main()
{
	// test1();
	// test2();
	// test3();
	//test4();
	//test5();
	test6();
	return 0;
}